package com.cc.demo.aspect;

import com.cc.demo.channel.TransactionChannelConstant;
import com.cc.demo.constant.MessageResultEnum;
import com.cc.demo.dto.TransactionMessage;
import com.cc.demo.mesage.service.MessageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

@Aspect
@Slf4j
@RequiredArgsConstructor
public class TransactionalMessageConfiguration {

    @Autowired
    @Qualifier(TransactionChannelConstant.TRANSACTION_COMPLETED)
    private MessageChannel output;

    private final MessageService messageService;

    @Pointcut("@annotation(org.springframework.cloud.stream.annotation.StreamListener) && execution (* com.cc.demo.listener..*Listener.*(org.springframework.messaging.Message+))")
    public void aspect() {
    }

    @Around("aspect()")
    Object recordAndConsume(ProceedingJoinPoint joinPoint) throws Throwable {
        // TODO 先进行幂等性校验, 这里的幂等性如何判断(id + destination?)

        // TODO 执行业务
        Object proceed = joinPoint.proceed();


        //处理ACK的情况
        //TODO 考虑ACK可以是非必须的(在生产者方进行定义，center中需要进行处理)，目前考虑一定是需要ACK的
        Optional<Method> method = Arrays.stream(joinPoint.getSignature().getDeclaringType().getMethods())
                .filter(m -> StringUtils.equals(m.getName(), joinPoint.getSignature().getName())).findFirst();
        if (method.isPresent()) {
            // 处理ACK的情况，判断是否有ID
            Message<?> message = (Message<?>) joinPoint.getArgs()[0];
            TransactionMessage<?> transactionMessage = (TransactionMessage<?>) message.getPayload();
            // 判断是否有ID，有ID需要ACK
            Integer id = transactionMessage.getId();
            if (Objects.nonNull(id)) {
                transactionMessage.setReceivedTime(messageService.getReceivedTime(message).orElse(null));
                transactionMessage.setExecutedTime(new Date());
                transactionMessage.setDuration(messageService.getDuration(message).orElse(null));
                transactionMessage.setDestination(messageService.getDestinationName(message).orElse(null));
                transactionMessage.setMessageResult(MessageResultEnum.SUCCESS.name());
                //处理完成之后需要ACK，使用message的方式进行ACK
                output.send(MessageBuilder.withPayload(message.getPayload()).build());
            } else {
                //如果没有ID，说明是外部系统的message进入
                //TODO 应该存一条message到center中
                log.info("接收到外部进来的message");
            }

        }
        return proceed;
    }


    @AfterThrowing(throwing = "ex", pointcut = "aspect()")
    public void doRecoveryActions(JoinPoint joinPoint, Throwable ex) {
        log.error("error message:{}", ex.getMessage());
    }


    @After("aspect()")
    public void after(JoinPoint joinPoint) {

    }

    @AfterReturning("aspect()")
    public void afterReturning(JoinPoint joinPoint) {
        System.out.println("test");
    }


}
